﻿/// 矩阵基本操作
/// 参考 [Opencv中Mat矩阵相乘——点乘、dot、mul运算详解](https://blog.csdn.net/dcrmg/article/details/52404580)

#include "opencv2/core.hpp"
#include "iostream"

using namespace std;
using namespace cv;


static inline
std::ostream& operator << (std::ostream& out, const std::vector<uchar>& vec)
{
    if (vec.size() == 0) {
        out << "[]";
    } else {
        out << "[";
        unsigned int i;
        for (i =0; i < vec.size()-1; i++) {
            out << int(vec[i]) << ", ";
        }
        out << int(vec[i]) << "]";
    }
    return out;
}

/// Mat的基础操作
void basicMatOp()
{
    cout << "\n通过 arr[4][3] 初始话矩阵 srcData, 类型CV_8UC1（1channel uchar）" << endl;
    uchar arr[4][3] = { { 1, 1,1 },{ 2, 2,2 },{ 3, 3,3 },{ 4, 4, 4 } };
    // 1. Mat的初始话
    Mat srcData(4, 3, CV_8UC1, arr);
    cout << "srcData=\n" << srcData << endl;

    // 2. Mat <-> Vector
    cout << "\n将数据 srcData.reshape(1, 1) 转成vector<uchar> " << endl;
    vector<uchar> v = (vector<uchar>)srcData.reshape(1, 1); //通道数不变，按行转为一行
    cout << "vector v=\n" << v << endl;

    cout << "\n将数据 dest = Mat(v); dest2 = dest.reshape(1, 4).clone(); 修改第一条数据，查看数据是否改变 " << endl;
    cv::Mat dest = Mat(v).reshape(1, 1);
    Mat dest2 = dest.reshape(1, 4).clone();  // 不clone，和dest指向的数据一致
    arr[0][0] = 8;  // srcData改变
    v[3] = 6; // dest改变
    dest.at<uchar>(0, 3) = 7;
    cout << "vector v=\n" << v << endl;
    cout << "srcData=\n" << srcData << endl;
    cout << "dest=\n" << dest << endl;
    cout << "dest2=\n" << dest2 << endl;
    cout << "v 和 dest 指向一个数据，但和srcData不是一个" << endl;

    // 3. Mat <-> array
    cout << "\n通过 << 方式 初始化 Mat B" << endl;
    cv::Mat B = (cv::Mat_<float>(2, 1) << 0.4404, 0.3111);
    cout << "B=" << B << endl;
    // 数据访问方式
    float a = B.at<float>(0, 0);
    float b = B.at<float>(1, 0);
    cout << "B(0,0):" << a << " B(1,0):" << b << endl;


    // 4. 数组内容传递给Mat, 注意cbuf修改以后，img也对对应修改了
    unsigned char cbuf[3][8] = {0};
    cv::Mat img(3, 8, CV_8UC1, (unsigned char*)cbuf);
    cbuf[0][0]= 1;
    cout << "通过数组直接生成 imag = \n" << img << endl;


    // 5. 按照行访问数据
    cout << "\n 按照行访问数据" << endl;
    Mat C(4, 3, CV_8UC1, arr);
    cout << "C=\n" << C << endl;
    uchar *cdata = C.ptr<uchar>(2);
    cout << "第3行:" << int(cdata[0]) << int(cdata[1]) << int(cdata[2]) << endl;

    // 对于二维vector的传值，我们可以这样处理​​​​​​​

    // uchar **array = new uchar*[mat.rows];
    // for (int i=0; i<mat.rows; ++i)
    //     array[i] = new uchar[mat.cols];

    // for (int i=0; i<mat.rows; ++i)
    //     array[i] = mat.ptr<uchar>(i);

    // RGB图与数组转换（3维矩阵）
    /**
    BYTE* iPtr = new BYTE [height*width*3];
	for(int i=0;i<height;i++)
	{
		for(int j=0;j<width;j++)
		{
			for(int k=0;k<3;k++)
			{
				iPtr[i*width*3+j*3+k] = img.at<Vec3b>(i,j)[k];
			}
		}
	}
    // 其中，img是一个3维uchar的Mat，Vec3b代表3个uchar，对于灰度图、4维矩阵等，只要把通道数和at的数据类型改一下就可以套用以上格式。还有一点千万注意，Mat的（i，j）是按（行，列）的规则，而图像中则是（高，宽），跟Size(x,y)，Rect(x,y)的（x，y）是不同的
    */

}
///
/// \brief 矩阵相乘 A*B
///
void demoMultiply()
{
    cout << "定义两个Mat矩阵A和B点乘，A为2行3列，B为3行2列：" << endl;
    Mat A=Mat::ones(2,3,CV_32FC1);
    Mat B=Mat::ones(3,2,CV_32FC1);
    Mat AB;

    A.at<float>(0,0)=1;
    A.at<float>(0,1)=2;
    A.at<float>(0,2)=3;

    A.at<float>(1,0)=4;
    A.at<float>(1,1)=5;
    A.at<float>(1,2)=6;

    B.at<float>(0,0)=1;
    B.at<float>(0,1)=2;

    B.at<float>(1,0)=3;
    B.at<float>(1,1)=4;

    B.at<float>(2,0)=5;
    B.at<float>(2,1)=6;

    AB=A*B;

    cout<<"A=\n"<<A<<endl<<endl;
    cout<<"B=\n"<<B<<endl<<endl;
    cout<<"A*B=\n\t"<<AB<<endl<<endl;
}

void demoDot()
{
    cout << "A.dot(B)操作相当于数学向量运算中的点乘，也叫向量的内积、数量积：" << endl;
    Mat A=Mat::ones(2,3,CV_8UC1);
    Mat B=Mat::ones(2,3,CV_8UC1);

    A.at<uchar>(0,0)=1;
    A.at<uchar>(0,1)=2;
    A.at<uchar>(0,2)=3;
    A.at<uchar>(1,0)=4;
    A.at<uchar>(1,1)=5;
    A.at<uchar>(1,2)=6;

    B.at<uchar>(0,0)=1;
    B.at<uchar>(0,1)=2;
    B.at<uchar>(0,2)=3;
    B.at<uchar>(1,0)=4;
    B.at<uchar>(1,1)=5;
    B.at<uchar>(1,2)=6;

    double AB=A.dot(B);

    cout<<"A=\n"<<A<<endl<<endl;
    cout<<"B=\n"<<B<<endl<<endl;
    cout<<u8"double类型的AB点乘 A.dot(B) =\n"<<AB<<endl<<endl;
}

void demoMul()
{
    Mat A=Mat::ones(2,3,CV_8UC1);
    Mat B=Mat::ones(2,3,CV_8UC1);

    A.at<uchar>(0,0)=60;
    A.at<uchar>(0,1)=2;
    A.at<uchar>(0,2)=3;
    A.at<uchar>(1,0)=4;
    A.at<uchar>(1,1)=5;
    A.at<uchar>(1,2)=6;

    B.at<uchar>(0,0)=60;
    B.at<uchar>(0,1)=2;
    B.at<uchar>(0,2)=3;
    B.at<uchar>(1,0)=4;
    B.at<uchar>(1,1)=5;
    B.at<uchar>(1,2)=6;

    Mat AB=A.mul(B);

//    AB中第一个元素应该为60*60=360，但AB默认的类型为CV_8UC1,即最大值只能是255；所以执行mul运算一定要定义AB足够的精度，防止溢出。
    cout<<"A=\n"<<A<<endl<<endl;
    cout<<"B=\n"<<B<<endl<<endl;
    cout<<"A.mul(B)=\n"<<AB<<endl<<endl;

}

void memFreeCase()
{
    cv::Mat A;
    cout << "A is " << A.empty() << endl;
    A.create(3, 3, CV_32FC1);
    cout << "A:" << A << endl;
    A.create(4, 4, CV_8U); // 前面生成的3x3 自动被释放掉
    cout << "A:" << A << endl;
    A.release();
    cout << "A is " << A.empty() << endl;
}

int main(int argc,char *argv[])
{
    int option = 1;
    switch (option)
    {
    case 0:
    {
        basicMatOp();
        cout << "---" << endl;
        demoMultiply();
        cout << "---" << endl;
        demoDot();
        cout << "---" << endl;
        demoMul();
    }
        break;
    case 1:
        memFreeCase();
        break;
    
    case 22:
    {
        Vec3d a(5.1141, -202.052, 1184.56);
        Vec3d b(9.33112, -8.94132, 1136.1);
        double l1 = norm(a, NORM_L2);
        double l = norm(a, b, NORM_L2);
        cout << "l1: " << l1 << endl;
        cout << "dis: " << l << endl;
    }
        break;

    default:
        break;
    }

    // system("pause");
}
